package com.dongzili.postgresql.common.exception;


import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.SaTokenContextException;
import cn.hutool.extra.servlet.ServletUtil;
import com.dongzili.postgresql.common.aspect.TraceIdUtils;
import com.dongzili.postgresql.common.vo.R;
import com.dongzili.postgresql.common.vo.ResponseCode;
import com.fasterxml.jackson.core.JsonParseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 96511
 * @date 2022/9/23 16:45
 * @description SaToken全局异常拦截
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常拦截，鉴权失败不会报错，会返回给前端报错原因
      * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) {
        if (request == null){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
        System.out.println(request.getContentType());
        if (request.getContentType()!=null && request.getContentType().contains("text/event-stream")){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
        R<?> ars = generalHandleException(new RuntimeException(e), request);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ars);
    }

    @ExceptionHandler(Throwable.class)
    public ResponseEntity<?> handlerThrowable(Throwable e, HttpServletRequest request, HttpServletResponse response) {
        R<?> ars = generalHandleException(new RuntimeException(e), request);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ars);
    }



    private static Throwable getRootCause(Throwable throwable) {
        Throwable rootCause = throwable;
        while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
            rootCause = rootCause.getCause();
        }
        return rootCause;
    }

    /**
     * 统一异常处理方法
     * @param e
     * @param request
     * @return
     */
    public static R<?> generalHandleException(Exception e, HttpServletRequest request) {

        log.error("{} - {} 全局异常信息 ex={}", request.getRequestURI(), ServletUtil.getClientIP(request), e.getMessage(), e.getCause());
        String traceId = TraceIdUtils.getTraceId();
        Throwable rootCause = getRootCause(e);
        if(rootCause instanceof MethodArgumentNotValidException) {		//参数校验异常
            log.error("参数校验异常---------->>>", rootCause);
            MethodArgumentNotValidException ee = (MethodArgumentNotValidException) rootCause;
            List<ObjectError> allErrors = ee.getBindingResult().getAllErrors();
            String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
            String field = allErrors.stream().map(s-> Objects.requireNonNull(s.getCodes())[0]).collect(Collectors.joining(";"));
            return R.error(message, field).setTraceId(traceId);
        }else if(rootCause instanceof BusinessExceptionHandler){
            BusinessExceptionHandler handler = (BusinessExceptionHandler)rootCause;
            if (handler.getResponseCode().equals(ResponseCode.ERROR)){
                return R.error(rootCause.getLocalizedMessage(), handler.getData()).setTraceId(traceId);
            }
            return R.error(handler.getResponseCode(), handler.getData()).setTraceId(traceId);
        }
        else if(rootCause instanceof JsonParseException){
            return R.error(ResponseCode.JSON_PARSE_ERROR, rootCause.getMessage()).setTraceId(traceId);
        }
        else if(rootCause instanceof NotLoginException){
            return R.error(ResponseCode.INVALID_TOKEN, rootCause.getMessage()).setTraceId(traceId);
        }
        else if(rootCause instanceof NotPermissionException){
            return R.error(rootCause.getMessage()).setTraceId(traceId);
        }
        else if(rootCause instanceof SaTokenContextException){
            return R.error(rootCause.getMessage()).setTraceId(traceId);
        }
        else if(rootCause instanceof SQLIntegrityConstraintViolationException){

            if(rootCause.getMessage().contains("Duplicate entry")){
                String msg = rootCause.getMessage()==null?"":rootCause.getMessage().replace("Duplicate entry", "数据行重复");//.split("for key")[0];
                return R.error(ResponseCode.PARAM_DUPLICATE, msg).setTraceId(traceId);
            }
            return R.error(ResponseCode.PARAM_DUPLICATE, rootCause.getMessage()).setTraceId(traceId);
        }
        else {	// 普通异常, 输出：500 + 异常信息
            return R.error("系统异常，请联系管理员查询详细信息！", rootCause.getMessage()).setTraceId(traceId);
        }
    }
}
